"""
排班算法模块 - 使用遗传算法和模拟退火优化排班
功能: 根据老人需求和护理人员技能自动生成最优排班方案
使用表: elder_demand, attendant_skills, dic_nursing_actions, schedule_results, time_slots, caregiver_schedules
返回: 生成的排班结果保存到数据库
"""
import random  # 导入随机数模块，用于生成随机排班和变异操作
import uuid  # 导入UUID模块，用于生成唯一标识符
from uuid import uuid4  # 导入uuid4函数，用于生成随机UUID
import pandas as pd  # 导入pandas库，用于数据处理和分析
import os  # 导入操作系统模块，用于文件和路径操作
from dataclasses import dataclass, field  # 导入数据类装饰器和字段函数，用于定义数据类
from typing import List, Set, Dict  # 导入类型提示，用于增强代码可读性
import logging  # 导入日志模块，用于记录程序运行状态
import math  # 导入数学模块，用于数学计算
from datetime import datetime, date, timedelta  # 导入日期时间模块，用于处理日期和时间
from .models import db, Elderdemand, Attendantskills, ScheduleResult, TimeSlot, DicNursingAction, CaregiverSchedule  # 导入数据库模型
from config_params import params  # 导入算法参数配置
from contextlib import contextmanager  # 导入上下文管理器，用于安全事务处理
import cupy as cp  # 导入CuPy库，用于GPU加速计算
import time  # 导入时间模块，用于性能计时
import numpy as np  # 导入NumPy库，用于数值计算
from .redis_utils import set_update_flag, set_update_time  # 导入Redis工具函数，用于更新状态和时间

# 设置日志记录
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')  # 配置日志格式和级别

"""
GPU性能监控装饰器 - 监控GPU内存使用和执行时间
功能: 记录函数执行前后的GPU内存使用情况和执行时间
返回: 被装饰函数的返回值
"""
# 装饰器函数,用于诊断GPU利用率
def diagnose_gpu_utilization(func):  # 定义装饰器函数，接收一个函数作为参数
    def wrapper(*args, **kwargs):  # 定义包装函数，接收任意位置和关键字参数
        start = time.time()  # 记录开始时间

        # 获取初始内存使用情况
        start_mem = cp.cuda.runtime.memGetInfo()[1] - cp.cuda.runtime.memGetInfo()[0]  # 计算初始GPU内存使用量

        result = func(*args, **kwargs)  # 调用原始函数

        end = time.time()  # 记录结束时间

        # 获取结束时的内存使用情况
        end_mem = cp.cuda.runtime.memGetInfo()[1] - cp.cuda.runtime.memGetInfo()[0]  # 计算结束时GPU内存使用量

        print(f"运行 {func.__name__} 使用 {end - start:.2f} 秒")  # 打印函数运行时间
        print(f"内存使用变化: {(end_mem - start_mem) / 1024 ** 2:.2f} MB")  # 打印内存使用变化，转换为MB
        return result  # 返回原始函数的结果

    return wrapper  # 返回包装函数

"""
活动数据类 - 定义护理活动的属性
功能: 存储护理活动的名称、持续时间、所需技能等信息
对应表: dic_nursing_actions
字段: name(活动名称), duration(持续时间), required_skill(所需技能ID), depends_on(依赖活动)
"""
# 定义活动数据类
@dataclass  # 使用数据类装饰器，自动生成初始化方法、repr方法等
class Activity:  # 定义活动类
    name: str  # 活动名称，字符串类型
    duration: int  # 活动持续时间，整数类型，单位为分钟
    required_skill: str = None  # 所需技能ID，字符串类型，默认为None
    depends_on: List[str] = field(default_factory=list)  # 依赖活动列表，默认为空列表

"""
客户数据类 - 定义老人的属性
功能: 存储老人ID和所需护理活动
对应表: elder_demand
字段: elder(老人ID), actions(所需护理活动ID元组)
"""
# 定义客户(老人)数据类
@dataclass(frozen=True)  # 使用冻结数据类装饰器，使实例不可变
class Client:  # 定义客户类
    elder: str  # 老人ID，字符串类型
    actions: tuple  # 所需护理活动ID元组

    def __hash__(self):  # 定义哈希方法，使对象可哈希
        return hash((self.elder, self.actions))  # 返回老人ID和活动元组的哈希值

    def __eq__(self, other):  # 定义相等比较方法
        if not isinstance(other, type(self)):  # 如果other不是Client类型
            return NotImplemented  # 返回NotImplemented，表示无法比较
        return self.elder == other.elder and self.actions == other.actions  # 比较老人ID和活动元组是否相等

"""
护理人员数据类 - 定义护理人员的属性
功能: 存储护理人员ID、姓名和技能
对应表: attendant_skills
字段: id(护理人员ID), attendant_name(姓名), skills(技能ID集合)
"""
# 定义护理人员数据类
@dataclass(frozen=True)  # 使用冻结数据类装饰器，使实例不可变
class CaregiverData:  # 定义护理人员类
    id: str  # 护理人员ID，字符串类型
    attendant_name: str  # 护理人员姓名，字符串类型
    skills: frozenset  # 定义护工技能集合字段,类型为frozenset(不可变集合)

    def __hash__(self):  # 定义哈希方法，使对象可哈希
        return hash((self.id, self.attendant_name, self.skills))  # 返回ID、姓名和技能集合的哈希值

    def __eq__(self, other):  # 定义相等比较方法
        if not isinstance(other, CaregiverData):  # 如果other不是CaregiverData类型
            return NotImplemented  # 返回NotImplemented，表示无法比较
        return (self.id, self.attendant_name, self.skills) == (other.id, other.attendant_name, other.skills)  # 比较ID、姓名和技能集合是否相等

"""
时间表数据类 - 定义时间段
功能: 存储开始时间并格式化为时间段字符串
对应表: time_slots
字段: start_time(开始时间,浮点数表示小时)
方法: __str__(返回格式化的时间段字符串,如"08:00-08:30")
"""
# 定义时间表数据类
@dataclass  # 使用数据类装饰器，自动生成初始化方法、repr方法等
class Schedule:  # 定义时间表类
    start_time: float  # 开始时间，浮点数类型，表示小时数（如8.5表示8:30）

    def __str__(self):  # 定义字符串表示方法
        start_hour = int(self.start_time)  # 获取开始小时部分
        start_minute = int((self.start_time % 1) * 60)  # 获取开始分钟部分
        end_time = self.start_time + 0.5  # 计算结束时间（半小时后）
        end_hour = int(end_time)  # 获取结束小时部分
        end_minute = int((end_time % 1) * 60)  # 获取结束分钟部分
        return f"{start_hour:02d}:{start_minute:02d}-{end_hour:02d}:{end_minute:02d}"  # 返回格式化的时间段字符串，如"08:00-08:30"

"""
计划基因数据类 - 定义排班的基本单元
功能: 将时间段、护理人员、老人和活动关联起来
字段: schedule(时间段), caregiver(护理人员), client(老人), activity(活动)
方法: __str__(返回格式化的排班信息字符串)
"""
# 定义计划基因数据类
@dataclass  # 使用数据类装饰器，自动生成初始化方法、repr方法等
class PlanGenes:  # 定义计划基因类
    schedule: Schedule  # 时间段，Schedule类型
    caregiver: CaregiverData  # 护理人员，CaregiverData类型
    client: Client  # 老人，Client类型
    activity: Activity  # 活动，Activity类型

    def __str__(self):  # 定义字符串表示方法
        return (f"TimeSlot: {self.schedule}\n"  # 返回时间段信息
                f"  Caregiver: {self.caregiver.attendant_name} (Skills: {self.caregiver.skills})\n"  # 返回护理人员信息
                f"  Client: {self.client.elder} (Actions: {self.client.actions})\n"  # 返回老人信息
                f"  Activity: {self.activity.name} (Duration: {self.activity.duration} mins, Required Skill: {self.activity.required_skill})\n")  # 返回活动信息

"""
排班方案数据类 - 定义一个完整的排班方案
功能: 存储排班基因列表和适应度评分
字段: order(排班基因列表), fitness(适应度评分), id(唯一标识符)
"""
# 定义订单数据类
@dataclass  # 使用数据类装饰器，自动生成初始化方法、repr方法等
class Order:  # 定义排班方案类
    order: List[PlanGenes]  # 排班基因列表，PlanGenes类型的列表
    fitness: int = 0  # 适应度评分，整数类型，默认为0
    id: str = field(default_factory=lambda: str(uuid.uuid4()))  # 唯一标识符，字符串类型，默认生成UUID

"""
排班计划类 - 实现排班算法的核心逻辑
功能: 加载数据、生成排班、优化排班、保存结果
方法:
    __init__: 初始化参数和数据
    _load_elder_actions: 从数据库加载老人的护理需求
    load_nursing_actions: 从数据库加载护理活动
    _init_clients_and_caregivers: 初始化老人和护理人员数据
    generate_population: 生成初始排班方案
    cal_fitness: 计算排班方案的适应度
    select_parents: 选择父代排班方案
    simulated_annealing_crossover: 使用模拟退火进行交叉操作
    crossover_and_mutation: 执行交叉和变异操作
    save_to_database: 将排班结果保存到数据库
    run: 运行排班算法
"""
# 主要的计划类
class Plan:  # 定义排班计划类
    """
    初始化方法 - 设置算法参数和初始化数据
    功能: 加载配置参数、初始化数据结构、加载数据库数据
    参数: params(算法参数字典)
    """
    def __init__(self, params):  # 初始化方法，接收参数字典
        self.client_num = params['client_num']  # 老人数量，从配置参数中获取
        self.caregiver_num = params['caregiver_num']  # 护理人员数量，从配置参数中获取
        self.population_size = params['population_size']  # 种群大小，遗传算法中的方案数量
        self.crossover_rate = params['crossover_rate']  # 交叉率，控制交叉操作的概率
        self.mutation_rate = params['mutation_rate']  # 变异率，控制变异操作的概率
        self.phenotype_limit = params['phenotype_limit']  # 表现型限制，控制每个方案中的基因数量
        self.tournament_size = int(self.population_size * 0.5)  # 锦标赛大小，用于父代选择的参数
        self.elimination_size = params['elimination_size']  # 淘汰大小，每代淘汰的方案数量
        self.meal_activities = frozenset([8, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73])  # 用餐活动ID集合，不可变集合提高性能
        self.nursing_actions = self.load_nursing_actions()  # 加载护理活动数据，从数据库获取
        self.clients = []  # 初始化老人列表，将在_init_clients_and_caregivers方法中填充
        self.caregivers = []  # 初始化护理人员列表，将在_init_clients_and_caregivers方法中填充
        self.orders = []  # 初始化排班方案列表，将在generate_population方法中填充
        self.emergency_slots = self.reserve_emergency_slots()  # 预留紧急时间段，用于处理突发情况
        self.sleep_times = frozenset([0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 12, 12.5, 13, 13.5, 14])  # 睡眠时间段集合，表示老人通常睡眠的时间
        self.meal_times = {8: "Breakfast", 11.5: "Lunch", 18: "Dinner"}  # 用餐时间映射，键为时间点，值为用餐类型
        self.elder_meal_activities = {}  # 初始化老人用餐活动字典，将在_init_elder_meal_activities方法中填充
        self.default_caregiver = self.get_default_caregiver()  # 获取默认护理人员，用于无法分配时的备选
        self.lack_caregiver = self.get_lack_caregiver()  # 获取缺乏技能的护理人员，用于特殊情况
        self._init_clients_and_caregivers()  # 初始化老人和护理人员数据，从数据库加载
        if not self.clients:  # 检查是否成功加载了老人数据
            logging.error("没有加载客户端，检查数据库或初始化进程。")  # 记录错误日志
            raise ValueError("没有可用于调度的客户端。")  # 抛出异常，终止程序
        self._init_elder_meal_activities()  # 初始化老人用餐活动，为每个老人分配用餐活动
        self.initial_temperature = params['initial_temperature']  # 模拟退火算法的初始温度
        self.cooling_rate = params['cooling_rate']  # 模拟退火算法的冷却率
        self.temperature = self.initial_temperature  # 当前温度，初始化为初始温度
        self.elder_actions: Dict[str, Set[int]] = self._load_elder_actions()  # 加载老人护理需求，返回字典
        self.all_nursing_actions = list(self.nursing_actions.keys())  # 所有护理活动ID列表，用于随机选择
        self.gpu_block_size = params.get('gpu_block_size', 256)  # GPU计算的块大小，用于并行计算
        self.caregiver_availability = {}  # 初始化护理人员可用性字典
        self._init_caregiver_availability()  # 初始化护理人员可用性数据
        self.generate_population()  # 生成初始种群，创建多个排班方案

        # 初始化CUDA上下文
        self.device = cp.cuda.Device(0)  # 选择第一个GPU设备
        self.device.use()  # 设置当前设备为选中的GPU

    """
    加载老人活动需求 - 从数据库获取老人需要的护理活动
    功能: 查询elder_demand表,获取每个老人的护理需求
    返回: 老人ID到护理活动ID集合的字典
    """
    def _load_elder_actions(self):
        elder_actions = {}  # 初始化结果字典，键为老人ID，值为护理活动ID集合
        elder_demands = Elderdemand.query.all()  # 查询所有老人需求记录
        for demand in elder_demands:  # 遍历每条需求记录
            if demand.elder not in elder_actions:  # 如果老人ID不在结果字典中
                elder_actions[demand.elder] = set()  # 为该老人创建一个空集合
            if demand.actions:  # 如果有护理活动数据
                action_ids = [int(action_id.strip()) for action_id in demand.actions.split(',') if action_id.strip()]  # 解析逗号分隔的活动ID字符串为整数列表
                elder_actions[demand.elder].update(action_ids)  # 将活动ID添加到该老人的集合中
            else:  # 如果没有护理活动数据
                logging.warning(f"找不到护理动作 {demand.elder}")  # 记录警告日志
        return elder_actions  # 返回结果字典

    """
    加载护理活动 - 从数据库获取所有护理活动
    功能: 查询dic_nursing_actions表,获取所有护理活动的详细信息
    返回: 护理活动ID到Activity对象的字典
    """
    def load_nursing_actions(self):
        nursing_actions = {}  # 初始化结果字典，键为活动ID，值为Activity对象
        actions = DicNursingAction.query.all()  # 查询所有护理活动记录
        for action in actions:  # 遍历每条活动记录
            nursing_actions[action.id] = Activity(  # 创建Activity对象并添加到字典
                name=action.name,  # 活动名称
                duration=30,  # 活动持续时间，固定为30分钟
                required_skill=str(action.id)  # 所需技能ID，使用活动ID作为技能ID
            )
        if 8 not in nursing_actions:  # 如果没有ID为8的饮食活动
            nursing_actions[8] = Activity(name="饮食", duration=30, required_skill="8")  # 添加默认的饮食活动
        logging.info(f"Loaded nursing actions: {[(id, action.name) for id, action in nursing_actions.items()]}")  # 记录加载的活动信息
        return nursing_actions  # 返回结果字典

    """
    预留紧急时间段 - 为紧急情况预留时间段
    功能: 设置不进行常规排班的时间段,用于应对紧急情况
    返回: 预留时间段的集合
    """
    def reserve_emergency_slots(self):
        return [random.randint(0, 47) for _ in range(6)]  # 随机选择6个时间段作为紧急时间段，范围是0-47（对应24小时的48个半小时时间段）

    """
    初始化老人和护理人员 - 创建Client和CaregiverData对象
    功能: 从数据库加载老人和护理人员数据,创建对应的对象
    表: elder_demand, attendant_skills
    """
    def _init_clients_and_caregivers(self):
        elder_demands = Elderdemand.query.filter_by(arrange='1').all()  # 查询需要排班的老人(arrange='1')
        elder_actions = {}  # 初始化老人活动字典，键为老人ID，值为活动ID集合
        for demand in elder_demands:  # 遍历每条老人需求记录
            if demand.elder not in elder_actions:  # 如果老人ID不在字典中
                elder_actions[demand.elder] = set()  # 为该老人创建一个空集合
            if demand.actions:  # 如果有护理活动数据
                action_ids = [int(action_id.strip()) for action_id in demand.actions.split(',') if
                              action_id.strip().isdigit()]  # 解析逗号分隔的活动ID字符串为整数列表，确保只包含数字
                for action_id in action_ids:  # 遍历每个活动ID
                    if action_id in self.nursing_actions:  # 如果活动ID在已加载的护理活动中
                        elder_actions[demand.elder].add(action_id)  # 将活动ID添加到该老人的集合中
                    else:  # 如果活动ID不在已加载的护理活动中
                        logging.warning(f"护理动作ID {action_id} 未在老人需求表中 {demand.elder}")  # 记录警告日志
            else:  # 如果没有护理活动数据
                logging.warning(f"护理动作不在老人需求表中 {demand.elder}")  # 记录警告日志

        self.clients = [  # 创建Client对象列表
            Client(elder=elder, actions=tuple(actions))  # 为每个老人创建一个Client对象，actions转换为元组以便哈希
            for elder, actions in elder_actions.items()  # 遍历老人活动字典
        ]
        logging.info(f"Loaded {len(self.clients)} clients")  # 记录加载的老人数量
        
        caregivers = Attendantskills.query.all()  # 查询所有护理人员记录
        self.caregivers = []  # 初始化护理人员列表
        for cg in caregivers:  # 遍历每条护理人员记录
            if cg.skills:  # 如果有技能数据
                skills = frozenset(int(skill) for skill in cg.skills.split(',') if skill.strip().isdigit())  # 解析逗号分隔的技能ID字符串为整数集合，确保只包含数字
            else:  # 如果没有技能数据
                skills = frozenset()  # 创建空集合
                logging.warning(f"没有找到合适的护理员 {cg.attendant_name} (ID: {cg.id})")  # 记录警告日志

            caregiver = CaregiverData(id=cg.id, attendant_name=cg.attendant_name, skills=skills)  # 创建CaregiverData对象
            self.caregivers.append(caregiver)  # 将护理人员添加到列表中

        logging.info(f"Loaded {len(self.caregivers)} caregivers")  # 记录加载的护理人员数量

    """
    获取默认护理人员 - 创建默认护理人员对象
    功能: 创建一个具有所有技能的默认护理人员,用于填补无法分配的时间段
    返回: 默认CaregiverData对象
    """
    def get_default_caregiver(self):
        all_skills = frozenset(action_id for action_id in self.nursing_actions.keys())  # 创建包含所有护理活动ID的技能集合
        return CaregiverData(id='0', attendant_name='None', skills=all_skills)  # 返回具有所有技能的默认护理人员

    """
    获取缺乏技能的护理人员 - 创建用于特殊情况的护理人员对象
    功能: 创建一个具有所有技能但标记为"lack"的护理人员
    返回: 特殊CaregiverData对象
    """
    def get_lack_caregiver(self):
        all_skills = frozenset(action_id for action_id in self.nursing_actions.keys())  # 创建包含所有护理活动ID的技能集合
        return CaregiverData(id='-1', attendant_name='lack', skills=all_skills)  # 返回具有所有技能但标记为"lack"的护理人员

    """
    初始化老人用餐活动 - 为每个老人设置用餐活动
    功能: 根据老人ID和用餐时间创建用餐活动
    """
    def _init_elder_meal_activities(self):
        default_meal_activity = min(self.meal_activities)  # 获取默认用餐活动ID（使用最小的用餐活动ID）
        for client in self.clients:  # 遍历每个老人
            elder_actions = set(client.actions)  # 获取老人的所有活动ID
            meal_actions = elder_actions.intersection(self.meal_activities)  # 计算老人活动与用餐活动的交集
            if meal_actions:  # 如果老人有用餐活动
                self.elder_meal_activities[client.elder] = list(meal_actions)[0]  # 使用第一个用餐活动
            else:  # 如果老人没有用餐活动
                self.elder_meal_activities[client.elder] = default_meal_activity  # 使用默认用餐活动
                logging.warning(
                    f"没给老人安排饮食动作 {client.elder}. 使用默认: {default_meal_activity}")  # 记录警告日志

    """
    获取睡眠活动 - 创建睡眠活动对象
    功能: 创建表示睡眠的Activity对象
    返回: 睡眠Activity对象
    """
    def get_sleep_activity(self):
        sleep_activity = next((a for a in self.nursing_actions.values() if a.name == '睡眠'), None)  # 在已加载的护理活动中查找名为"睡眠"的活动
        if sleep_activity is None:  # 如果没有找到睡眠活动
            logging.warning("未找到睡眠动作，自动创建一个睡眠动作")  # 记录警告日志
            return Activity('睡眠', 30, required_skill='10')  # 创建一个默认的睡眠活动，持续30分钟，所需技能ID为10
        return sleep_activity  # 返回找到的睡眠活动

    """
    获取用餐活动 - 创建用餐活动对象
    功能: 根据老人ID和时间创建用餐活动对象
    参数: elder(老人ID), time(用餐时间)
    返回: 用餐Activity对象
    """
    def get_meal_activity(self, elder, time):
        if time in self.meal_times:  # 如果当前时间是用餐时间
            activity_id = self.elder_meal_activities.get(elder, 8)  # 获取老人的用餐活动ID，如果没有则使用默认值8
            activity = self.nursing_actions.get(activity_id)  # 根据活动ID获取Activity对象
            if not activity:  # 如果没有找到对应的Activity对象
                activity = Activity(name="饮食", duration=30, required_skill="8")  # 创建一个默认的用餐活动
            logging.info(f"Meal activity for elder {elder} at time {time}: {activity.name} (ID: {activity_id})")  # 记录信息日志
            return activity  # 返回用餐活动
        return None  # 如果不是用餐时间，返回None

    """
    选择活动 - 为老人选择合适的护理活动
    功能: 根据老人需求和已安排的活动,选择下一个要安排的活动
    参数: client(老人对象), time(时间), scheduled_actions(已安排的活动)
    返回: 选中的Activity对象
    """
    def select_activity(self, client, time, scheduled_actions):
        if time in self.sleep_times:  # 如果当前时间是睡眠时间
            return self.get_sleep_activity()  # 返回睡眠活动

        if time in self.meal_times:  # 如果当前时间是用餐时间
            return self.get_meal_activity(client.elder, time)  # 返回用餐活动

        remaining_actions = set(client.actions) - scheduled_actions  # 计算老人未安排的活动集合
        possible_activities = [self.nursing_actions[action_id] for action_id in remaining_actions
                               if action_id in self.nursing_actions and action_id not in self.meal_activities]  # 获取可能的活动列表，排除用餐活动
        if not possible_activities:  # 如果没有可能的活动
            logging.warning(f"No possible activities for client {client.elder} at time {time}")  # 记录警告日志
            return None  # 返回None
        
        selected_activity = random.choice(possible_activities)  # 随机选择一个活动
        logging.info(f"Selected activity {selected_activity.name} for client {client.elder} at time {time}")  # 记录信息日志
        return selected_activity  # 返回选中的活动

    """
    生成初始种群 - 创建初始排班方案
    功能: 随机生成多个排班方案作为初始种群
    返回: 初始化self.orders列表
    """
    def generate_population(self):
        for _ in range(self.population_size):  # 循环生成指定数量的排班方案
            self.reset_caregiver_availability()  # 重置护理人员可用性
            plan_genes_list = []  # 初始化排班基因列表
            for client in self.clients:  # 遍历每个老人
                daily_schedule = []  # 初始化老人的日程表
                scheduled_actions = set()  # 初始化已安排的活动集合
                for current_time in range(48):  # 遍历48个时间段（一天24小时，每半小时一个时间段）
                    time = current_time / 2  # 将索引转换为实际时间（0.0, 0.5, 1.0, ...）
                    if time in self.meal_times:  # 如果是用餐时间
                        activity = self.get_meal_activity(client.elder, time)  # 获取用餐活动
                        caregiver = self.select_caregiver(client, time, activity)  # 选择护理人员
                    elif time in self.sleep_times:  # 如果是睡眠时间
                        activity = self.get_sleep_activity()  # 获取睡眠活动
                        caregiver = self.default_caregiver  # 使用默认护理人员
                    else:  # 其他时间
                        activity = self.select_activity(client, time, scheduled_actions)  # 选择活动
                        caregiver = self.select_caregiver(client, time, activity) if activity else None  # 如果有活动，选择护理人员

                    if activity:  # 如果有活动
                        if caregiver is None:  # 如果没有分配到护理人员
                            caregiver = self.default_caregiver  # 使用默认护理人员
                        schedule = Schedule(start_time=time)  # 创建时间段对象
                        plan_genes = PlanGenes(schedule=schedule, caregiver=caregiver, client=client, activity=activity)  # 创建排班基因
                        daily_schedule.append(plan_genes)  # 将排班基因添加到日程表
                        scheduled_actions.add(int(activity.required_skill))  # 将活动的所需技能添加到已安排的活动集合
                        logging.info(
                            f"Scheduled for client {client.elder} at time {time}: Activity {activity.name}, Caregiver {caregiver.attendant_name}")  # 记录信息日志
                    else:  # 如果没有活动
                        logging.warning(f"No suitable activity found for client {client.elder} at time {time}")  # 记录警告日志

                plan_genes_list.extend(daily_schedule)  # 将老人的日程表添加到排班基因列表

            order = Order(order=plan_genes_list)  # 创建排班方案
            self.orders.append(order)  # 将排班方案添加到方案列表

        logging.info(f"Generated population with {len(self.orders)} orders")

    """
    创建排班条目 - 创建排班数据字典
    功能: 将时间、活动和护理人员信息组合成排班条目
    参数: current_time(时间), activity(活动), caregiver(护理人员)
    返回: 排班条目字典
    """
    def create_schedule_entry(self, current_time, activity, caregiver):
        start_hour = int(current_time)  # 获取开始小时
        start_minute = int((current_time % 1) * 60)  # 获取开始分钟
        end_time = current_time + 0.5  # 计算结束时间（半小时后）
        end_hour = int(end_time)  # 获取结束小时
        end_minute = int((end_time % 1) * 60)  # 获取结束分钟
        return {  # 返回排班条目字典
            'TimeSlot': f"{start_hour:02d}:{start_minute:02d}-{end_hour:02d}:{end_minute:02d}",  # 格式化时间段字符串，如"08:00-08:30"
            'Activity': activity.name if activity else "No Activity",  # 活动名称，如果没有活动则为"No Activity"
            'ActivityId': str(activity.required_skill) if activity and activity.required_skill else "0",  # 活动ID，如果没有活动或所需技能则为"0"
            'Caregiver': caregiver.attendant_name if caregiver else "No Caregiver",  # 护理人员姓名，如果没有护理人员则为"No Caregiver"
            'CaregiverId': str(caregiver.id) if caregiver and caregiver.id else "0"  # 护理人员ID，如果没有护理人员或ID则为"0"
        }

    """
    选择护理人员 - 为特定活动选择最合适的护理人员
    功能: 根据技能匹配度、工作负荷等因素选择护理人员
    参数: client(老人对象), time(时间), activity(活动)
    返回: 选中的CaregiverData对象
    """
    def select_caregiver(self, client, time, activity):
        required_skill = int(activity.required_skill) if activity.required_skill.isdigit() else None  # 获取活动所需的技能ID，如果不是数字则为None
        time_slot = int(time * 2)  # 将时间转换为时间段索引（0-47）

        available_caregivers = [  # 筛选可用的护理人员
            cg for cg in self.caregivers  # 遍历所有护理人员
            if self.is_caregiver_available(cg.id, time_slot) and (required_skill is None or required_skill in cg.skills)  # 检查护理人员是否可用且具有所需技能
        ]

        if not available_caregivers:  # 如果没有可用的护理人员
            if activity.name == "睡眠":  # 如果是睡眠活动
                return self.default_caregiver  # 使用默认护理人员
            else:  # 如果是其他活动
                logging.warning(f"护理员资源不足，无法为客户 {client.elder} 在时间 {time} 分配所需技能 {required_skill} 的护理员。使用 'lack' 护理员。")  # 记录警告日志
                return self.lack_caregiver  # 使用缺乏技能的护理人员

        selected_caregiver = random.choice(available_caregivers)  # 随机选择一个可用的护理人员
        self.set_caregiver_unavailable(selected_caregiver.id, time_slot)  # 将选中的护理人员在该时间段标记为不可用
        return selected_caregiver  # 返回选中的护理人员

    """
    计算技能相关性得分 - 评估护理人员技能与所需技能的匹配度
    功能: 计算护理人员技能与活动所需技能的匹配程度
    参数: caregiver(护理人员), required_skill_id(所需技能ID)
    返回: 匹配度得分
    """
    def calculate_skill_relevance_score(self, caregiver, required_skill_id):
        """计算护理员技能相关度评分"""
        # 获取所需技能的父级ID
        required_skill_pid = self.get_skill_pid(required_skill_id)  # 获取所需技能的父级ID，用于判断技能的类别关系
        # 获取所需技能的层级
        required_skill_level = self.get_skill_level(required_skill_id)  # 获取所需技能的层级，用于判断技能的难度等级

        base_score = 0  # 初始化基础得分
        if required_skill_id in caregiver.skills:  # 如果护理人员具有完全匹配的技能
            # 完全匹配得满分
            base_score = 1.0  # 设置满分
        else:  # 如果没有完全匹配的技能
            # 检查护理员的所有技能
            for skill in caregiver.skills:  # 遍历护理人员的所有技能
                skill_pid = self.get_skill_pid(skill)  # 获取护理人员技能的父级ID
                skill_level = self.get_skill_level(skill)  # 获取护理人员技能的层级

                # 同父级技能加分
                if skill_pid == required_skill_pid:  # 如果护理人员的技能与所需技能属于同一类别
                    base_score += 0.5  # 加0.5分，表示技能类别相关

                # 同级别技能加分
                if skill_level == required_skill_level:  # 如果护理人员的技能与所需技能属于同一难度等级
                    base_score += 0.3  # 加0.3分，表示技能难度相当

                # 高级别技能对低级别需求加分
                if skill_level < required_skill_level:  # 如果护理人员的技能难度高于所需技能难度（数值越小表示难度越高）
                    base_score += 0.2  # 加0.2分，表示技能水平超过需求

        return min(base_score, 1.0)  # 确保评分不超过1.0，返回最终得分

    """
    获取技能父级ID - 查找技能的父级ID
    功能: 从护理动作字典中查找指定技能的父级ID
    参数: skill_id(技能ID)
    返回: 父级技能ID
    """
    def get_skill_pid(self, skill_id):
        """获取技能的父级ID"""
        for action in self.nursing_actions.values():  # 遍历所有护理活动
            if action.id == skill_id:  # 如果找到匹配的技能ID
                return action.pid  # 返回该技能的父级ID
        return None  # 如果没有找到匹配的技能ID，返回None

    """
    获取技能级别 - 查找技能的级别
    功能: 从护理动作字典中查找指定技能的级别
    参数: skill_id(技能ID)
    返回: 技能级别
    """
    def get_skill_level(self, skill_id):
        """获取技能的层级"""
        for action in self.nursing_actions.values():  # 遍历所有护理活动
            if action.id == skill_id:  # 如果找到匹配的技能ID
                return action.level  # 返回该技能的级别
        return None  # 如果没有找到匹配的技能ID，返回None

    """
    计算工作负荷平衡 - 评估护理人员的工作分配是否均衡
    功能: 计算护理人员的工作负荷得分,用于平衡工作分配
    参数: caregiver_id(护理人员ID)
    返回: 工作负荷平衡得分
    """
    def calculate_workload_balance(self, caregiver_id):
        """计算工作负载平衡度"""
        availability_array = self.caregiver_availability[caregiver_id]  # 获取护理人员的可用性数组
        workload = 1 - (sum(availability_array) / len(availability_array))  # 计算工作负荷，可用时间段比例的补集

        # 检查连续工作时间
        consecutive_work = 0  # 初始化连续工作时间计数器
        max_consecutive = 0  # 初始化最大连续工作时间
        for available in availability_array:  # 遍历可用性数组
            if not available:  # 如果时间段不可用（正在工作）
                consecutive_work += 1  # 连续工作时间加1
                max_consecutive = max(max_consecutive, consecutive_work)  # 更新最大连续工作时间
            else:  # 如果时间段可用（休息）
                consecutive_work = 0  # 重置连续工作时间计数器

        # 根据最大连续工作时间调整工作负载评分
        workload_score = 1 - (workload * 0.7 + (max_consecutive / 8) * 0.3)  # 计算工作负荷平衡得分，考虑总工作量(70%)和连续工作时间(30%)
        return max(0, min(workload_score, 1.0))  # 确保得分在0到1之间，返回最终得分

    """
    重新分配护理人员 - 尝试为活动分配新的护理人员
    功能: 当原护理人员不可用时,尝试分配其他合适的护理人员
    参数: elder(老人ID), time(时间), activity(活动)
    返回: 新分配的CaregiverData对象
    """
    def reassign_caregiver(self, elder, time, activity):
        """优化后的护理员重分配策略"""
        try:
            client = next(c for c in self.clients if c.elder == elder)  # 根据老人ID查找对应的Client对象
            time_slot = int(time * 2)  # 将时间转换为时间段索引（0-47）
            required_skill = int(activity.required_skill) if activity.required_skill.isdigit() else None  # 获取活动所需的技能ID

            if required_skill is None:  # 如果没有所需技能
                return self.select_caregiver(client, time, activity)  # 使用普通的选择护理人员方法

            # 特殊处理睡眠活动
            if activity.name == "睡眠":  # 如果是睡眠活动
                return self.default_caregiver  # 使用默认护理人员

            # 特殊处理用餐相关活动（pid=8的护理动作）
            if self.get_skill_pid(required_skill) == 8:  # 如果是用餐相关活动
                # 优先选择有用餐照护经验的护理员
                meal_care_skills = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73}  # 用餐照护相关技能ID集合
                priority_caregivers = [  # 筛选有用餐照护经验的可用护理人员
                    cg for cg in self.caregivers
                    if self.is_caregiver_available(cg.id, time_slot) and
                       any(skill in cg.skills for skill in meal_care_skills)  # 检查是否有任何用餐照护技能
                ]
                if priority_caregivers:  # 如果有符合条件的护理人员
                    selected_caregiver = random.choice(priority_caregivers)  # 随机选择一个
                    self.set_caregiver_unavailable(selected_caregiver.id, time_slot)  # 将选中的护理人员在该时间段标记为不可用
                    return selected_caregiver  # 返回选中的护理人员

            # 计算所有可用护理员的综合评分
            available_caregivers = []  # 初始化可用护理人员列表
            for caregiver in self.caregivers:  # 遍历所有护理人员
                if not self.is_caregiver_available(caregiver.id, time_slot):  # 如果护理人员在该时间段不可用
                    continue  # 跳过此护理人员

                # 技能相关度评分（占比50%）
                skill_score = self.calculate_skill_relevance_score(caregiver, required_skill)  # 计算技能相关度得分

                # 工作负载平衡评分（占比30%）
                workload_score = self.calculate_workload_balance(caregiver.id)  # 计算工作负荷平衡得分

                # 护理级别匹配评分（占比20%）
                level_score = 1.0 if self.get_skill_level(required_skill) in {  # 检查所需技能的级别是否与护理人员的任何技能级别匹配
                    self.get_skill_level(skill) for skill in caregiver.skills
                } else 0.0  # 如果匹配则得分1.0，否则得分0.0

                # 计算综合评分
                total_score = (  # 计算加权综合评分
                        skill_score * 0.5 +  # 技能相关度占50%
                        workload_score * 0.3 +  # 工作负荷平衡占30%
                        level_score * 0.2  # 护理级别匹配占20%
                )

                available_caregivers.append((caregiver, total_score))  # 将护理人员和评分添加到可用护理人员列表

            if not available_caregivers:  # 如果没有可用的护理人员
                logging.warning(  # 记录警告日志
                    f"重分配失败: 无可用护理员 - 老人: {elder}, "
                    f"时间: {time}, 活动: {activity.name}"
                )
                return self.lack_caregiver  # 返回缺乏技能的护理人员

            # 选择评分最高的护理员
            selected_caregiver = max(available_caregivers, key=lambda x: x[1])[0]  # 根据评分选择最佳护理人员
            self.set_caregiver_unavailable(selected_caregiver.id, time_slot)  # 将选中的护理人员在该时间段标记为不可用

            logging.info(  # 记录信息日志
                f"重分配成功: 老人: {elder}, "
                f"护理员: {selected_caregiver.attendant_name}, "
                f"时间: {time}, 活动: {activity.name}, "
                f"技能要求: {required_skill}"
            )

            return selected_caregiver  # 返回选中的护理人员

        except Exception as e:  # 捕获可能的异常
            logging.error(f"重分配过程发生错误: {str(e)}")  # 记录错误日志
            return self.lack_caregiver  # 返回缺乏技能的护理人员

    """
    检查护理人员可用性 - 判断护理人员在特定时间段是否可用
    功能: 查询护理人员在指定时间段的可用状态
    参数: caregiver_id(护理人员ID), time_slot(时间段)
    返回: 布尔值,表示是否可用
    """
    def is_caregiver_available(self, caregiver_id, time_slot):
        return self.caregiver_availability[caregiver_id][time_slot]  # 返回护理人员在指定时间段的可用状态

    """
    设置护理人员不可用 - 标记护理人员在特定时间段不可用
    功能: 更新护理人员在指定时间段的可用状态
    参数: caregiver_id(护理人员ID), time_slot(时间段)
    """
    def set_caregiver_unavailable(self, caregiver_id, time_slot):
        self.caregiver_availability[caregiver_id][time_slot] = False  # 将护理人员在指定时间段的可用状态设置为False（不可用）

    """
    计算适应度 - 评估排班方案的质量
    功能: 使用GPU加速计算每个排班方案的适应度评分
    返回: 更新self.orders中每个方案的fitness值
    """
    @diagnose_gpu_utilization
    def cal_fitness(self):
        all_order_data = [  # 创建包含所有排班基因数据的列表
            (int(gene.activity.required_skill), hash(gene.caregiver), hash(gene.client))  # 提取活动所需技能ID、护理人员哈希值和老人哈希值
            for order in self.orders  # 遍历所有排班方案
            for gene in order.order  # 遍历方案中的所有排班基因
        ]

        print(f"all_order_data length: {len(all_order_data)}")  # 打印数据长度，用于调试

        if not all_order_data:  # 如果没有数据
            print("Warning: all_order_data is empty")  # 打印警告信息
            return 0  # 返回0，表示没有适应度

        all_order_data = cp.array(all_order_data, dtype=cp.int64)  # 将数据转换为CuPy数组，以便在GPU上处理

        print(f"CuPy array shape: {all_order_data.shape}")  # 打印数组形状，用于调试

        try:
            # 奖励非默认活动
            non_default_activity = (all_order_data[:, 0] != 10).astype(cp.int32) * 5  # 如果活动ID不是10（默认活动），则加5分
            print(f"Sum of non_default_activity: {cp.sum(non_default_activity)}")  # 打印非默认活动得分总和，用于调试

            # 放宽技能匹配逻辑
            skill_match = (all_order_data[:, 0] % 10 == all_order_data[:, 1] % 10).astype(cp.int32) * 10  # 如果活动ID和护理人员ID的个位数相同，则加10分
            print(f"Sum of skill_match: {cp.sum(skill_match)}")  # 打印技能匹配得分总和，用于调试

            # 奖励客户多样性（如果连续的活动属于不同的客户）
            client_diversity = (all_order_data[1:, 2] != all_order_data[:-1, 2]).astype(cp.int32) * 8  # 如果相邻活动的老人不同，则加8分
            client_diversity = cp.concatenate([cp.array([0]), client_diversity])  # 为第一个活动添加0，保持数组长度一致
            print(f"Sum of client_diversity: {cp.sum(client_diversity)}")  # 打印客户多样性得分总和，用于调试

            # 奖励活动多样性
            activity_diversity = (all_order_data[1:, 0] != all_order_data[:-1, 0]).astype(cp.int32) * 5  # 如果相邻活动的ID不同，则加5分
            activity_diversity = cp.concatenate([cp.array([0]), activity_diversity])  # 为第一个活动添加0，保持数组长度一致
            print(f"Sum of activity_diversity: {cp.sum(activity_diversity)}")  # 打印活动多样性得分总和，用于调试

            # 奖励稀有活动（假设活动ID越大越稀有）
            rare_activity = (all_order_data[:, 0] // 10).astype(cp.float32)  # 将活动ID除以10，以减小数值
            rare_activity = rare_activity / cp.max(rare_activity) * 20  # 归一化并乘以20，得到稀有活动得分
            print(f"Sum of rare_activity: {cp.sum(rare_activity)}")  # 打印稀有活动得分总和，用于调试

            # 总适应度是所有因素的和
            combined_fitness = non_default_activity + skill_match + client_diversity + activity_diversity + rare_activity  # 计算总适应度
            print(f"Sum of combined_fitness: {cp.sum(combined_fitness)}")  # 打印总适应度总和，用于调试

            # 计算每个订单的适应度
            genes_per_order = len(self.orders[0].order)  # 假设每个订单的基因数量相同，获取第一个排班方案的基因数量
            fitness_values = cp.sum(combined_fitness.reshape(-1, genes_per_order), axis=1)  # 将适应度数组重塑为每个排班方案一行，并按行求和
            print(f"fitness_values shape: {fitness_values.shape}")  # 打印适应度数组形状，用于调试
            print(f"Max fitness value: {cp.max(fitness_values)}")  # 打印最大适应度值，用于调试
            print(f"Min fitness value: {cp.min(fitness_values)}")  # 打印最小适应度值，用于调试
            print(f"Average fitness value: {cp.mean(fitness_values)}")  # 打印平均适应度值，用于调试
            print(f"Fitness value standard deviation: {cp.std(fitness_values)}")  # 打印适应度标准差，用于调试
        except Exception as e:  # 捕获可能的异常
            print(f"Error during fitness calculation: {str(e)}")  # 打印错误信息
            return 0  # 返回0，表示计算失败

        fitness_values_cpu = fitness_values.get()  # 将GPU上的适应度数组复制到CPU内存

        for order, fitness in zip(self.orders, fitness_values_cpu):  # 遍历所有排班方案和对应的适应度值
            order.fitness = int(fitness)  # 更新排班方案的适应度属性

        avg_fitness = np.mean(fitness_values_cpu)  # 计算平均适应度
        print(f"平均适应度: {avg_fitness}")  # 打印平均适应度，用于调试
        return avg_fitness  # 返回平均适应度

    """
    选择父代 - 选择用于交叉的排班方案
    功能: 使用锦标赛选择法选择高适应度的排班方案
    返回: 选中的父代排班方案
    """
    def select_parents(self):
        # 将选择移到GPU
        fitness_values = cp.array([order.fitness for order in self.orders], dtype=cp.float32)  # 将所有排班方案的适应度转换为CuPy数组
        sorted_indices = cp.argsort(fitness_values)[::-1]  # 按适应度降序排序，获取排序后的索引
        selected_indices = sorted_indices[:self.tournament_size].get()  # 选择适应度最高的前tournament_size个方案的索引，并复制到CPU内存
        self.orders = [self.orders[i] for i in selected_indices]  # 更新排班方案列表，只保留选中的方案

    """
    模拟退火交叉 - 使用模拟退火算法进行交叉操作
    功能: 结合两个父代排班方案生成新的排班方案
    参数: parent1(父代1), parent2(父代2)
    返回: 新生成的排班方案
    """
    @diagnose_gpu_utilization
    def simulated_annealing_crossover(self, parent1, parent2):
        crossover_point = random.randint(0, len(parent1.order))  # 随机选择交叉点
        child = Order(order=parent1.order[:crossover_point] + parent2.order[crossover_point:])  # 创建子代，前半部分来自父代1，后半部分来自父代2

        # 将退火过程移到GPU
        child_data = cp.array([(int(gene.schedule.start_time * 2), int(gene.activity.required_skill),
                                hash(gene.client), hash((gene.caregiver.id, gene.caregiver.attendant_name)))
                               for gene in child.order], dtype=cp.int64)  # 将子代数据转换为CuPy数组，包含时间段、活动ID、老人哈希值和护理人员哈希值
        temperature = cp.float32(self.temperature)  # 将当前温度转换为CuPy浮点数

        # 使用cupy进行随机数生成和向量操作
        random_values = cp.random.random(len(child_data))  # 生成随机数数组，用于决定是否变异
        mutation_mask = cp.exp(-1 / temperature) > random_values  # 根据温度和随机数创建变异掩码，温度越高变异概率越大

        mutated_times = cp.random.randint(0, 48, size=len(child_data))  # 生成随机时间段索引
        mutated_activities = cp.random.randint(0, len(self.nursing_actions), size=len(child_data))  # 生成随机活动ID

        child_data[mutation_mask, 0] = mutated_times[mutation_mask]  # 对变异的基因更新时间段
        child_data[mutation_mask, 1] = mutated_activities[mutation_mask]  # 对变异的基因更新活动ID

        # 将结果传回CPU
        mutated_indices = cp.where(mutation_mask)[0].get()  # 获取变异基因的索引

        # 基于GPU计算更新子代order
        for i in mutated_indices:  # 遍历所有变异的基因
            gene = child.order[i]  # 获取当前基因
            current_time = child_data[i, 0] / 2  # 将时间段索引转换为实际时间
            if current_time not in self.sleep_times:  # 如果不是睡眠时间
                new_activity_id = int(child_data[i, 1])  # 获取新的活动ID
                new_activity = self.nursing_actions.get(new_activity_id,
                                                        self.select_activity(gene.client, current_time, set()))  # 获取新的活动对象，如果ID无效则选择一个新活动
                if new_activity:  # 如果有有效的活动
                    new_caregiver = self.select_caregiver(gene.client, current_time, new_activity)  # 为新活动选择护理人员
                    if new_caregiver:  # 如果成功选择了护理人员
                        child.order[i] = PlanGenes(Schedule(current_time), new_caregiver, gene.client, new_activity)  # 更新子代的基因

        return child  # 返回生成的子代

    """
    交叉和变异 - 生成新一代排班方案
    功能: 对选中的父代进行交叉和变异操作,生成新的排班方案
    返回: 更新self.orders列表
    """
    @diagnose_gpu_utilization
    def crossover_and_mutation(self):
        orders = self.orders.copy()  # 复制当前的排班方案列表
        while len(orders) < self.population_size:  # 循环直到生成足够数量的排班方案
            if random.random() < self.crossover_rate:  # 根据交叉率决定是否进行交叉操作
                parent1, parent2 = random.sample(orders, 2)  # 随机选择两个父代
                child1 = self.simulated_annealing_crossover(parent1, parent2)  # 生成第一个子代
                child2 = self.simulated_annealing_crossover(parent2, parent1)  # 生成第二个子代
                
                orders.extend([child1, child2])  # 将生成的子代添加到排班方案列表

            mutation_count = int(self.population_size * self.mutation_rate)  # 计算需要变异的方案数量
            mutation_indices = cp.random.choice(len(orders), size=mutation_count, replace=False)  # 随机选择要变异的方案索引

            for i in mutation_indices.get():  # 遍历所有要变异的方案索引
                self.reset_caregiver_availability()  # 重置护理员可用性
                order = orders[i]  # 获取当前要变异的排班方案

                for gene in order.order:  # 遍历方案中的所有基因
                    time_slot = int(gene.schedule.start_time * 2)  # 将时间转换为时间段索引
                    self.set_caregiver_unavailable(gene.caregiver.id, time_slot)  # 将护理人员在该时间段标记为不可用

                gene_index = random.randint(0, len(order.order) - 1)  # 随机选择一个基因进行变异
                gene = order.order[gene_index]  # 获取选中的基因
                current_time = gene.schedule.start_time  # 获取当前时间

                if current_time in self.sleep_times:  # 如果是睡眠时间
                    new_activity = self.get_sleep_activity()  # 获取睡眠活动
                    new_caregiver = self.default_caregiver  # 使用默认护理人员
                elif current_time in self.meal_times:  # 如果是用餐时间
                    meal_activity = self.get_meal_activity(gene.client.elder, current_time)  # 获取用餐活动
                    if meal_activity:  # 如果有用餐活动
                        new_activity = meal_activity  # 使用用餐活动
                        new_caregiver = self.select_caregiver(gene.client, current_time, new_activity)  # 选择护理人员
                    else:  # 如果没有用餐活动
                        new_activity = self.select_activity(gene.client, current_time, set())  # 选择其他活动
                        new_caregiver = self.select_caregiver(gene.client, current_time,
                                                              new_activity) if new_activity else None  # 如果有活动，选择护理人员
                else:  # 如果是其他时间
                    scheduled_actions = set(
                        int(g.activity.required_skill) for g in order.order if g.client == gene.client)  # 获取已安排的活动集合
                    required_actions = set(self.elder_actions[gene.client.elder])  # 获取老人需要的所有活动
                    unscheduled_actions = required_actions - scheduled_actions - self.meal_activities - {10}  # 计算未安排的活动集合，排除用餐活动和睡眠活动

                    if unscheduled_actions:  # 如果有未安排的活动
                        new_activity_id = random.choice(list(unscheduled_actions))  # 随机选择一个未安排的活动
                        new_activity = self.nursing_actions[new_activity_id]  # 获取活动对象
                    else:  # 如果没有未安排的活动
                        new_activity = self.select_activity(gene.client, current_time, scheduled_actions)  # 选择其他活动

                    new_caregiver = self.select_caregiver(gene.client, current_time,
                                                          new_activity) if new_activity else None  # 如果有活动，选择护理人员

                if new_activity:  # 如果有有效的活动
                    if new_caregiver is None:  # 如果没有分配到护理人员
                        new_caregiver = self.default_caregiver  # 使用默认护理人员
                    new_gene = PlanGenes(gene.schedule, new_caregiver, gene.client, new_activity)  # 创建新的基因
                    order.order[gene_index] = new_gene  # 更新基因

                    # 更新护理员可用性
                    time_slot = int(new_gene.schedule.start_time * 2)  # 将时间转换为时间段索引
                    self.set_caregiver_unavailable(new_caregiver.id, time_slot)  # 将护理人员在该时间段标记为不可用
                else:  # 如果没有有效的活动
                    order.order.pop(gene_index)  # 删除该基因

                # 重新评估订单的适应度
                order.fitness = self.calculate_order_fitness(order)  # 计算排班方案的适应度

        # 根据适应度对订单进行排序，并保留前 population_size 个订单
        orders.sort(key=lambda x: x.fitness, reverse=True)  # 按适应度降序排序
        self.orders = orders[:self.population_size]  # 保留适应度最高的前population_size个方案

        # 更新温度
        self.temperature *= self.cooling_rate  # 降低温度，用于模拟退火算法

    """
    验证排班 - 检查排班方案是否有效
    功能: 验证排班方案是否满足各种约束条件
    参数: client_schedules(排班方案)
    返回: 布尔值,表示排班方案是否有效
    """
    def validate_schedule(self, client_schedules):
        for elder, schedule in client_schedules.items():  # 遍历每个老人的排班
            caregiver_schedules = {}  # 初始化护理人员排班字典
            for time_slot, entry in enumerate(schedule):  # 遍历每个时间段
                if entry:  # 如果有排班条目
                    caregiver = entry['Caregiver']  # 获取护理人员
                    if caregiver not in caregiver_schedules:  # 如果护理人员不在字典中
                        caregiver_schedules[caregiver] = set()  # 为该护理人员创建一个空集合
                    if time_slot in caregiver_schedules[caregiver]:  # 如果护理人员在该时间段已被分配
                        logging.warning(
                            f"时间冲突: 护理员 {caregiver} 在时间段 {time_slot / 2} 被重复分配给老人 {elder}")  # 记录警告日志
                        # 这里可以添加解决冲突的逻辑，例如重新分配护理员
                        new_caregiver = self.reassign_caregiver(elder, time_slot / 2, entry['Activity'])  # 重新分配护理人员
                        if new_caregiver:  # 如果成功分配了新的护理人员
                            entry['Caregiver'] = new_caregiver.attendant_name  # 更新护理人员姓名
                            entry['CaregiverId'] = new_caregiver.id  # 更新护理人员ID
                        else:  # 如果没有可用的护理人员
                            entry['Caregiver'] = "未分配"  # 设置护理人员为"未分配"
                            entry['CaregiverId'] = "0"  # 设置护理人员ID为"0"
                    caregiver_schedules[caregiver].add(time_slot)  # 将时间段添加到护理人员的排班集合中

    """
    安全事务管理器 - 提供数据库事务的上下文管理
    功能: 确保数据库操作在事务中执行,出错时回滚
    参数: session(数据库会话)
    """
    @staticmethod
    @contextmanager
    def safe_transaction(session):
        try:
            yield  # 执行上下文中的代码
            session.commit()  # 如果没有异常，提交事务
        except Exception:  # 捕获任何异常
            session.rollback()  # 回滚事务
            raise  # 重新抛出异常

    """
    保存到数据库 - 将生成的排班结果保存到数据库
    功能: 将排班结果保存到schedule_results和time_slots表
    参数: client_schedules(排班方案)
    表: schedule_results, time_slots
    """
    def save_to_database(self, client_schedules):
        current_datetime = datetime.now()  # 获取当前日期时间

        for elder, schedule in client_schedules.items():  # 遍历每个老人的排班
            try:
                with self.safe_transaction(db.session):  # 使用安全事务管理器
                    logging.info(f"Processing schedule for elder {elder}")  # 记录信息日志

                    if schedule is None:  # 如果排班为None
                        logging.warning(f"Schedule for elder {elder} is None")  # 记录警告日志
                        continue  # 跳过此老人

                    if not isinstance(schedule, list):  # 如果排班不是列表
                        logging.warning(f"Schedule for elder {elder} is not a list. Type: {type(schedule)}")  # 记录警告日志
                        continue  # 跳过此老人

                    valid_schedule = []  # 初始化有效排班列表
                    for i, slot in enumerate(schedule):  # 遍历每个时间段
                        if slot is None:  # 如果时间段为None
                            logging.warning(f"Slot {i} for elder {elder} is None")  # 记录警告日志
                            continue  # 跳过此时间段
                        if not isinstance(slot, dict):  # 如果时间段不是字典
                            logging.warning(f"Slot {i} for elder {elder} is not a dict. Type: {type(slot)}")  # 记录警告日志
                            continue  # 跳过此时间段
                        if 'TimeSlot' not in slot:  # 如果时间段没有'TimeSlot'键
                            logging.warning(f"Slot {i} for elder {elder} does not have 'TimeSlot' key. Keys: {slot.keys()}")  # 记录警告日志
                            continue  # 跳过此时间段
                        valid_schedule.append(slot)  # 将有效的时间段添加到列表中

                    if not valid_schedule:  # 如果没有有效的时间段
                        logging.warning(f"No valid slots for elder {elder}")  # 记录警告日志
                        continue  # 跳过此老人

                    sorted_schedule = sorted(valid_schedule, key=lambda x: datetime.strptime(x['TimeSlot'].split('-')[0], '%H:%M'))  # 按时间排序

                    existing_result = ScheduleResult.query.filter_by(  # 查询是否存在当天的排班结果
                        elder=elder,
                        schedule_date=current_datetime.date()
                    ).first()

                    if existing_result:  # 如果存在排班结果
                        existing_result.schedule_data = sorted_schedule  # 更新排班数据
                        existing_result.schedule_date = current_datetime  # 更新日期
                        TimeSlot.query.filter_by(schedule_result_id=existing_result.id).delete()  # 删除旧的时间段
                    else:  # 如果不存在排班结果
                        existing_result = ScheduleResult(  # 创建新的排班结果
                            id=str(uuid4()),
                            elder=elder,
                            schedule_date=current_datetime,
                            schedule_data=sorted_schedule
                        )
                        db.session.add(existing_result)  # 添加到数据库会话

                    db.session.flush()  # 刷新会话，获取ID

                    time_slots_to_add = []  # 初始化要添加的时间段列表
                    for index, slot in enumerate(sorted_schedule):  # 遍历排序后的时间段
                        if 'TimeSlot' in slot and 'Activity' in slot and 'ActivityId' in slot and 'Caregiver' in slot and 'CaregiverId' in slot:  # 检查时间段是否包含所有必要的键
                            new_time_slot = TimeSlot(  # 创建新的时间段
                                id=str(uuid4()),
                                schedule_result_id=existing_result.id,
                                elder=elder,
                                time_slot=slot['TimeSlot'],
                                activity=slot['Activity'],
                                activity_id=slot['ActivityId'],
                                caregiver='lack' if slot['CaregiverId'] == '-1' else slot['Caregiver'],
                                caregiver_id=slot['CaregiverId'],
                                time_order=index,
                                schedule_date=current_datetime
                            )
                            time_slots_to_add.append(new_time_slot)  # 将时间段添加到列表中
                        else:  # 如果时间段缺少必要的键
                            logging.warning(f"Incomplete slot data for elder {elder} at index {index}: {slot}")  # 记录警告日志

                    db.session.bulk_save_objects(time_slots_to_add)  # 批量保存时间段

                logging.info(f"Created/Updated schedule for elder {elder} on {current_datetime}")  # 记录信息日志

            except Exception as e:  # 捕获任何异常
                logging.error(f"Error saving schedules to database for elder {elder}: {str(e)}", exc_info=True)  # 记录错误日志

        logging.info(f"Successfully saved/updated schedules for {len(client_schedules)} elders on {current_datetime}")  # 记录信息日志

    """
    运行算法 - 执行排班算法的主要流程
    功能: 迭代执行种群生成、适应度计算、选择、交叉变异等操作
    返回: 生成最终的排班结果并保存到数据库
    """
    @diagnose_gpu_utilization
    def run(self):
        for generation in range(self.phenotype_limit):  # 迭代指定代数
            start_time = time.time()  # 记录开始时间
            self.batch_process()  # 使用批处理方法计算适应度
            self.select_parents()  # 选择父代
            end_time = time.time()  # 记录结束时间

            logging.info(f"Generation {generation + 1} took {end_time - start_time:.2f} seconds")  # 记录每代耗时

            logging.info(f"GPU memory usage: {cp.get_default_memory_pool().used_bytes() / 1024 ** 2:.2f} MB")  # 记录GPU内存使用情况

            logging.info(
                f"Best Fitness: {max(order.fitness for order in self.orders)}, Temperature: {self.temperature:.2f}")  # 记录当前最佳适应度和温度

            cp.cuda.Stream.null.synchronize()  # 同步GPU流，确保所有GPU操作完成

        self.cal_fitness()  # 最后一次计算适应度
        self.orders.sort(key=lambda x: x.fitness, reverse=True)  # 按适应度降序排序
        logging.info(f"最优解: {self.orders[0].fitness}")  # 记录最优解的适应度

        client_schedules = {}  # 初始化老人排班字典
        for client in self.clients:  # 遍历所有老人
            elder_demand = Elderdemand.query.filter_by(elder=client.elder, arrange='1').first()  # 查询老人是否需要排班
            if not elder_demand:  # 如果老人不需要排班
                logging.info(f"Skipping schedule generation for elder {client.elder} (arrange != '1')")  # 记录跳过信息
                continue  # 跳过此老人

            client_schedules[client.elder] = [None] * 48  # 初始化老人的48个时间段，每个时间段初始为None
            required_actions = set(client.actions) - self.meal_activities - {10}  # 计算老人需要的非用餐非睡眠活动
            scheduled_actions = set()  # 初始化已安排的活动集合

            # 安排睡眠活动
            for sleep_time in self.sleep_times:  # 遍历所有睡眠时间
                activity = self.get_sleep_activity()  # 获取睡眠活动
                caregiver = self.default_caregiver  # 使用默认护理人员
                time_index = int(sleep_time * 2)  # 将时间转换为时间段索引
                client_schedules[client.elder][time_index] = self.create_schedule_entry(sleep_time, activity,
                                                                                        caregiver)  # 创建并保存排班条目

            # 安排饮食活动
            for meal_time in self.meal_times:  # 遍历所有用餐时间
                activity = self.get_meal_activity(client.elder, meal_time)  # 获取用餐活动
                caregiver = self.select_caregiver(client, meal_time, activity)  # 选择护理人员
                time_index = int(meal_time * 2)  # 将时间转换为时间段索引
                client_schedules[client.elder][time_index] = self.create_schedule_entry(meal_time, activity,
                                                                                        caregiver)  # 创建并保存排班条目

            # 安排其他必需活动
            available_times = [t / 2 for t in range(48) if client_schedules[client.elder][t] is None]  # 获取所有未安排的时间段
            random.shuffle(available_times)  # 随机打乱可用时间，以便更均匀地分配活动

            for action_id in required_actions:  # 遍历所有必需活动
                if action_id not in scheduled_actions and available_times:  # 如果活动未安排且有可用时间
                    activity = self.nursing_actions[action_id]  # 获取活动对象
                    current_time = available_times.pop()  # 获取一个可用时间
                    caregiver = self.select_caregiver(client, current_time, activity)  # 选择护理人员
                    time_index = int(current_time * 2)  # 将时间转换为时间段索引
                    client_schedules[client.elder][time_index] = self.create_schedule_entry(current_time, activity,
                                                                                            caregiver)  # 创建并保存排班条目
                    scheduled_actions.add(action_id)  # 将活动添加到已安排集合
                    logging.info(f"为老人 {client.elder} 安排活动 {activity.name} 在时间 {current_time}")  # 记录安排信息

            # 检查是否所有必需活动都已安排
            unscheduled_actions = required_actions - scheduled_actions  # 计算未安排的必需活动
            if unscheduled_actions:  # 如果有未安排的必需活动
                logging.warning(f"无法为老人 {client.elder} 安排以下活动: {unscheduled_actions}")  # 记录警告信息

            # 填充剩余的空闲时间
            priority_activities = [154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
                                   169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179]  # 优先活动ID列表，如康复训练、娱乐活动等
            secondary_activities = [152, 153]  # 次要活动ID列表，如休息、自由活动等

            available_times = [t / 2 for t in range(48) if client_schedules[client.elder][t] is None]  # 重新获取所有未安排的时间段

            # 先尝试安排所有优先活动
            for action_id in priority_activities:  # 遍历所有优先活动
                if available_times:  # 如果有可用时间
                    activity = self.nursing_actions[action_id]  # 获取活动对象
                    current_time = available_times.pop(0)  # 获取第一个可用时间
                    caregiver = self.select_caregiver(client, current_time, activity)  # 选择护理人员
                    time_index = int(current_time * 2)  # 将时间转换为时间段索引
                    client_schedules[client.elder][time_index] = self.create_schedule_entry(current_time, activity,
                                                                                            caregiver)  # 创建并保存排班条目
                    logging.info(f"为老人 {client.elder} 安排优先活动 {activity.name} 在时间 {current_time}")  # 记录安排信息
                else:  # 如果没有可用时间
                    break  # 跳出循环

            # 更新可用时间
            available_times = [t / 2 for t in range(48) if client_schedules[client.elder][t] is None]  # 重新获取所有未安排的时间段

            # 如果还有剩余时间，再安排次要活动
            if available_times:  # 如果有可用时间
                for action_id in secondary_activities:  # 遍历所有次要活动
                    if available_times:  # 如果有可用时间
                        activity = self.nursing_actions[action_id]  # 获取活动对象
                        current_time = available_times.pop(0)  # 获取第一个可用时间
                        caregiver = self.select_caregiver(client, current_time, activity)  # 选择护理人员
                        time_index = int(current_time * 2)  # 将时间转换为时间段索引
                        client_schedules[client.elder][time_index] = self.create_schedule_entry(current_time,
                                                                                                activity, caregiver)  # 创建并保存排班条目
                        logging.info(f"为老人 {client.elder} 安排次要活动 {activity.name} 在时间 {current_time}")  # 记录安排信息
                    else:  # 如果没有可用时间
                        break  # 跳出循环

            # 如果还有剩余的空闲时间，用随机活动填充
            while available_times:  # 当还有可用时间时循环
                current_time = available_times.pop(0)  # 获取第一个可用时间
                random_activity_id = random.choice(self.all_nursing_actions)  # 随机选择一个活动ID
                activity = self.nursing_actions[random_activity_id]  # 获取活动对象
                caregiver = self.select_caregiver(client, current_time, activity)  # 选择护理人员
                time_index = int(current_time * 2)  # 将时间转换为时间段索引
                client_schedules[client.elder][time_index] = self.create_schedule_entry(current_time, activity,
                                                                                        caregiver)  # 创建并保存排班条目
                logging.info(f"为老人 {client.elder} 安排随机活动 {activity.name} 在时间 {current_time}")  # 记录安排信息

        # 验证并修正调度
        self.validate_schedule(client_schedules)  # 验证排班方案，检查并解决冲突
        self.save_to_database(client_schedules)  # 将排班方案保存到数据库

        # 添加这一行来保存护理人员的调度
        self.save_caregiver_schedules(date.today())  # 保存护理人员的排班情况到数据库

        best_order = max(self.orders, key=lambda x: x.fitness)  # 获取适应度最高的排班方案
        logging.info(f"最优解适应度: {best_order.fitness}")  # 记录最优解的适应度
        logging.info(f"最终退火温度: {self.temperature:.4f}")  # 记录最终的退火温度
        print(f"最优解适应度: {best_order.fitness}")  # 打印最优解的适应度
        print(f"最终退火温度: {self.temperature:.4f}")  # 打印最终的退火温度

        # 更新Redis中的时间和标志
        set_update_time()  # 设置更新时间，记录排班完成的时间
        set_update_flag()  # 设置更新标志，通知系统排班已更新
        
        return client_schedules  # 返回生成的排班方案

    """
    保存护理人员排班 - 将护理人员的排班情况保存到数据库
    功能: 将排班结果转换为护理人员视角并保存到caregiver_schedules表
    参数: schedule_date(排班日期)
    表: caregiver_schedules
    """
    def save_caregiver_schedules(self, schedule_date):
        """保存护理人员的调度到数据库"""
        for caregiver_id, availability in self.caregiver_availability.items():  # 遍历所有护理人员的可用性
            for time_slot, is_available in enumerate(availability):  # 遍历每个时间段
                # 将时间槽转换为实际时间
                slot_time = datetime.combine(schedule_date, datetime.min.time()) + timedelta(minutes=30 * time_slot)  # 计算时间段对应的具体时间

                # 检查是否已存在该护理人员在该日期和时间段的记录
                existing_schedule = CaregiverSchedule.query.filter_by(  # 查询数据库中是否存在记录
                    caregiver_id=caregiver_id,
                    schedule_date=slot_time.date(),
                    time_slot=time_slot
                ).first()

                if existing_schedule:  # 如果记录存在
                    # 如果记录存在，更新可用性和时间
                    existing_schedule.is_available = is_available  # 更新可用性
                    existing_schedule.schedule_date = slot_time  # 更新时间
                else:  # 如果记录不存在
                    # 如果记录不存在，创建新记录
                    new_schedule = CaregiverSchedule(  # 创建新的护理人员排班记录
                        caregiver_id=caregiver_id,
                        schedule_date=slot_time,
                        time_slot=time_slot,
                        is_available=is_available
                    )
                    db.session.add(new_schedule)  # 添加到数据库会话

        try:  # 尝试提交事务
            db.session.commit()  # 提交数据库事务
            logging.info(f"成功保存护理人员调度到数据库，日期：{schedule_date}")  # 记录成功信息
        except Exception as e:  # 捕获异常
            db.session.rollback()  # 回滚事务
            logging.error(f"保存护理人员调度时发生错误：{str(e)}")  # 记录错误信息

    """
    初始化护理人员可用性 - 设置护理人员的初始可用时间
    功能: 为每个护理人员创建时间可用性数组
    """
    def _init_caregiver_availability(self):
        for caregiver in self.caregivers:  # 遍历所有护理人员
            self.caregiver_availability[caregiver.id] = [True] * 48  # 48个30分钟时间段，初始化为全部可用

    """
    检查护理人员可用性 - 判断护理人员在特定时间段是否可用
    功能: 查询护理人员在指定时间段的可用状态
    参数: caregiver_id(护理人员ID), time_slot(时间段)
    返回: 布尔值,表示是否可用
    """
    def is_caregiver_available(self, caregiver_id: str, time_slot: int) -> bool:
        return self.caregiver_availability[caregiver_id][time_slot]  # 返回护理人员在指定时间段的可用状态

    """
    设置护理人员不可用 - 标记护理人员在特定时间段不可用
    功能: 更新护理人员在指定时间段的可用状态
    参数: caregiver_id(护理人员ID), time_slot(时间段)
    """
    def set_caregiver_unavailable(self, caregiver_id: str, time_slot: int):
        self.caregiver_availability[caregiver_id][time_slot] = False  # 将护理人员在指定时间段的可用状态设置为False（不可用）

    """
    重置护理人员可用性 - 将所有护理人员的可用性重置为默认状态
    功能: 清除之前的可用性设置,恢复到初始状态
    """
    def reset_caregiver_availability(self):
        for caregiver_id in self.caregiver_availability:  # 遍历所有护理人员ID
            self.caregiver_availability[caregiver_id] = [True] * 48  # 重置为48个时间段全部可用

    """
    批处理方法 - 使用GPU批量处理排班方案
    功能: 将排班方案分批移至GPU进行处理,提高计算效率
    参数: batch_size(批处理大小)
    """
    @diagnose_gpu_utilization
    def batch_process(self, batch_size=1000):
        for i in range(0, len(self.orders), batch_size):  # 按批次处理排班方案
            batch = self.orders[i:i + batch_size]  # 获取当前批次的排班方案

            # 将批次数据移到 GPU
            batch_data = []  # 初始化批次数据列表
            for order in batch:  # 遍历当前批次的排班方案
                for gene in order.order:  # 遍历排班方案中的所有基因
                    batch_data.append([  # 将基因数据添加到批次数据列表
                        int(gene.activity.required_skill),  # 活动所需技能ID
                        hash(gene.caregiver),  # 护理人员哈希值
                        hash(gene.client)  # 老人哈希值
                    ])

            if not batch_data:  # 如果批次数据为空
                logging.warning(f"Empty batch data for batch starting at index {i}")  # 记录警告日志
                continue  # 跳过此批次

            batch_data = cp.array(batch_data, dtype=cp.int64)  # 将批次数据转换为CuPy数组
            logging.info(f"Batch data shape: {batch_data.shape}")  # 记录批次数据形状

            try:
                # 在 GPU 上计算适应度
                condition1 = (batch_data[:, 0] != 10)  # 计算条件1：活动ID不是10（不是默认活动）
                logging.info(f"Condition1 shape: {condition1.shape}")  # 记录条件1形状
                condition2 = (batch_data[:, 0] == batch_data[:, 1])  # 计算条件2：活动ID等于护理人员哈希值（简化的技能匹配）
                logging.info(f"Condition2 shape: {condition2.shape}")  # 记录条件2形状
                combined_condition = condition1 & condition2  # 组合条件：同时满足条件1和条件2
                logging.info(f"Combined condition shape: {combined_condition.shape}")  # 记录组合条件形状

                genes_per_order = len(self.orders[0].order)  # 假设每个订单的基因数量相同，获取第一个排班方案的基因数量
                fitness_values = cp.sum(combined_condition.reshape(-1, genes_per_order), axis=1)  # 将组合条件重塑为每个排班方案一行，并按行求和得到适应度
                logging.info(f"Fitness values shape: {fitness_values.shape}")  # 记录适应度数组形状

                # 将结果移回 CPU 并更新订单
                fitness_values_cpu = fitness_values.get()  # 将GPU上的适应度数组复制到CPU内存
                for j, order in enumerate(batch):  # 遍历当前批次的排班方案
                    order.fitness = int(fitness_values_cpu[j])  # 更新排班方案的适应度属性

                logging.info(f"Processed batch of size {len(batch)} (orders {i} to {i + len(batch) - 1})")  # 记录处理完成的批次信息
                logging.info(f"Max fitness in batch: {cp.max(fitness_values)}")  # 记录批次中的最大适应度
                logging.info(f"Min fitness in batch: {cp.min(fitness_values)}")  # 记录批次中的最小适应度
                logging.info(f"Average fitness in batch: {cp.mean(fitness_values)}")  # 记录批次中的平均适应度

            except Exception as e:  # 捕获任何异常
                logging.error(f"Error processing batch: {str(e)}")  # 记录错误信息
                logging.error(f"Batch data: {batch_data}")  # 记录批次数据
                continue  # 跳过此批次

        # 更新温度
        self.temperature *= self.cooling_rate  # 降低温度，用于模拟退火算法

if __name__ == "__main__":  # 如果直接运行此文件
    plan = Plan(params)  # 创建Plan对象，使用config_params.py中定义的参数
    try:  # 尝试运行排班算法
        client_schedules = plan.run()  # 运行排班算法，获取排班结果
        output_directory = "/mnt/d/ruanjian/Anaconda3/tf2/suanfa/app/schedules"  # 设置输出目录
        os.makedirs(output_directory, exist_ok=True)  # 创建输出目录，如果已存在则不报错

        for elder, schedule in client_schedules.items():  # 遍历每个老人的排班
            df = pd.DataFrame(schedule, columns=['TimeSlot', 'Activity', 'Caregiver'])  # 创建DataFrame，包含时间段、活动和护理人员列
            output_file_name = f"{elder}_schedule.xlsx"  # 生成输出文件名
            output_file_path = os.path.join(output_directory, output_file_name)  # 生成完整的输出文件路径
            df.to_excel(output_file_path, index=False)  # 将排班结果保存为Excel文件，不包含索引列
            logging.info(f"Schedule for elder {elder} saved to {output_file_path}")  # 记录保存成功信息

    except Exception as e:  # 捕获任何异常
        logging.error(f"An error occurred: {str(e)}")  # 记录错误信息
    finally:  # 无论是否发生异常，都执行清理操作
        # 清理 GPU 内存
        mempool = cp.get_default_memory_pool()  # 获取默认内存池
        pinned_mempool = cp.get_default_pinned_memory_pool()  # 获取固定内存池
        mempool.free_all_blocks()  # 释放所有内存块
        pinned_mempool.free_all_blocks()  # 释放所有固定内存块
        logging.info("GPU memory cleaned up")  # 记录内存清理完成信息
